#include <stdexcept>
#include <string>
#include <algorithm>

#include "ring_poly.h"
#include "modular_reduction.h"
#include "utils.h"
#include "ring.h"
using namespace std;

Ring::Ring(uint64_t NParam, const std::vector<uint64_t> &Moduli) : N(0), allowsNTT(false), modulusBigint(nullptr)
{
    SetParameters(NParam, Moduli);
    SetBarrettParameters(NParam, Moduli);
    for (unsigned long qi : Modulus) {
        if (!IsPrime(qi)) {
            return;
        }
        if (qi % (2 * NParam) != 1) {
            allowsNTT = false;
            return;
        }
    }
    SetNTTParameters(NParam, Moduli);
}

Ring::~Ring()
{
    delete modulusBigint;
}
/*
 * @brief 设置Barrett约简算法所需的参数
 *
 * 该函数初始化用于Barrett模约简的必要参数。Barrett约简是一种通过预计算来优化模运算的算法。
 * 函数会验证输入参数的合法性，并为每个模数计算相应的Barrett约简参数。
 *
 * @param NBarrettParam 环的阶数，必须是大于等于16的2的幂次方
 * @param modulusParam 模数向量，包含所有互不相同的模数
 */
void Ring::SetBarrettParameters(uint64_t NBarrettParam, const std::vector<uint64_t> &modulusParam)
{
    int MAXLK = 50;
    if (NBarrettParam < 16 || ((NBarrettParam & (NBarrettParam - 1)) != 0 && NBarrettParam != 0)) {
        throw std::invalid_argument("invalid ring degree (must be a power of 2 >= 16)");
    }
    if (modulusParam.empty()) {
        throw std::invalid_argument("invalid modulus (must be a non-empty vector<uint64_t>)");
    }
    if (!AllDistinct(modulusParam)) {
        throw std::invalid_argument("invalid modulus (moduli are not distinct)");
    }
    N = NBarrettParam;
    M = N << 1;
    logN = log(N) / log(2);
    NthRoot = 2 * N;
    Modulus = modulusParam;
    uint64_t L = Modulus.size();
    if ((L == 0) || (L > MAXLK)) {
        cout << "内存空间开辟错误 " << endl;
    }
    twoqVec = new uint64_t[L]();
    qrVec = new __uint128_t[L];
    qrVecHigh = new uint64_t[L]();
    qrVecLow = new uint64_t[L]();

    for (long i = 0; i < L; i++) {
        qrVec[i] = ~static_cast<__uint128_t>(0) / Modulus[i];
        twoqVec[i] = static_cast<uint64_t>(2 * Modulus[i]);
        qrVecHigh[i] = static_cast<uint64_t>(qrVec[i] >> 64);
        qrVecLow[i] = static_cast<uint64_t>(qrVec[i]);
    }
}
/*
 * @brief 设置环的数论变换(NTT)参数
 *
 * @details 此函数初始化执行NTT运算所需的所有参数,
 * 包括单位根、其逆元以及为优化预先计算的值。
 *
 * @param NNttParam 环的度数参数(N)。必须是2的幂且大于等于16
 * @param modulusParam 模数值的向量。每个模数必须是素数且互不相同
 */
void Ring::SetNTTParameters(uint64_t NNttParam, const std::vector<uint64_t> &modulusParam)
{
    int MAXLK = 50;
    if (NNttParam < 16 || ((NNttParam & (NNttParam - 1)) != 0 && NNttParam != 0)) {
        throw std::invalid_argument("invalid ring degree (must be a power of 2 >= 16)");
    }
    if (modulusParam.empty()) {
        throw std::invalid_argument("invalid modulus (must be a non-empty vector<uint64_t>)");
    }
    if (!AllDistinct(modulusParam)) {
        throw std::invalid_argument("invalid modulus (moduli are not distinct)");
    }
    uint64_t L = Modulus.size();
    if ((L == 0) || (L > MAXLK)) {
        cout << "内存空间开辟错误 " << endl;
    }
    qdVec = new uint64_t[L];
    qRoots = new uint64_t[L];
    qRootsInv = new uint64_t[L];
    qRootPows = new uint64_t *[L];
    qRootPowsInv = new uint64_t *[L];
    nInvModq = new uint64_t[L];
    nScaleInvModq = new uint64_t[L];

    qRootPowsShoup = new uint64_t *[L];    // shoup
    qRootPowsInvShoup = new uint64_t *[L]; // shoup
    nInvModqShoup = new uint64_t[L];       // shoup

    for (long i = 0; i < L; i++) {
        qdVec[i] = Modulus[i] << 1;
        qRoots[i] = FindMthRootOfUnity(M, Modulus[i]);
        qRootsInv[i] = InvMod(qRoots[i], Modulus[i]);
        nInvModq[i] = InvMod((uint64_t)N, Modulus[i]);
        MulMod(nScaleInvModq[i], nInvModq[i], (static_cast<uint64_t>(1) << 32), Modulus[i]);
        MulMod(nScaleInvModq[i], nScaleInvModq[i], (static_cast<uint64_t>(1) << 32), Modulus[i]);
        qRootPows[i] = new uint64_t[N];
        qRootPowsInv[i] = new uint64_t[N];
        uint64_t power = static_cast<uint64_t>(1);
        uint64_t powerInv = static_cast<uint64_t>(1);
        qRootPowsShoup[i] = new uint64_t[N]();               // shoup参数
        qRootPowsInvShoup[i] = new uint64_t[N]();            // shoup参数
        nInvModqShoup[i] = x_Shoup(nInvModq[i], Modulus[i]); // shoup

        for (long j = 0; j < N; j++) {
            uint64_t jPrime = BitReverse(static_cast<uint32_t>(j)) >> (32 - logN);
            qRootPows[i][jPrime] = power;
            qRootPowsShoup[i][jPrime] = x_Shoup(power, Modulus[i]); // shoup参数
            qRootPowsInv[i][jPrime] = powerInv;
            qRootPowsInvShoup[i][jPrime] = x_Shoup(powerInv, Modulus[i]); // shoup参数
            if (j < N - 1) {
                MulMod(power, power, qRoots[i], Modulus[i]);
                MulMod(powerInv, powerInv, qRootsInv[i], Modulus[i]);
            }
        }
    }
}
/*
 * @brief 对多项式进行快速数论变换（NTT）。
 *
 * 该函数实现了对多项式 `p1` 的数论变换（NTT），并将结果存储在多项式 `p2` 中。
 * 分别对每一个模数的多项式调用iNTTAndEqual()进行NTT。
 * @param p1 输入的多项式，类型为 `Poly*`，其系数将被进行 NTT 操作。
 * @param p2 输出的多项式，类型为 `Poly*`，存储 NTT 结果。
 */
void Ring::NTT(Poly *p1, Poly *p2)
{
    Copy(p1, p2);
#pragma omp parallel for
    for (size_t i = 0; i < Modulus.size(); i++) {
        iNTTAndEqual(p2->Coeffs[i], i);
    }
}
/*
 * @brief 对多项式选择层数进行快速数论变换（NTT）。
 *
 * 该函数实现了对多项式 `p1` 的数论变换（NTT），并将结果存储在多项式 `p2` 中。
 * 分别对l个模数的多项式调用iNTTAndEqual()进行NTT。
 *
 * @param level 进行运算的最高层级(从0开始计数)
 * @param p1 输入的多项式，类型为 `Poly*`，其系数将被进行 NTT 操作。
 * @param p2 输出的多项式，类型为 `Poly*`，存储 NTT 结果。
 */
void Ring::NTTLvl(int level, Poly *p1, Poly *p2)
{
    Copy(p1, p2);
#pragma omp parallel for
    for (size_t x = 0; x < level + 1; x++) {
        iNTTAndEqual(p2->Coeffs[x], x);
    }
}
/*
 * @brief 对多项式进行快速逆数论变换（INTT）。
 *
 * 该函数实现了对多项式 `p1` 的逆数论变换（INTT），并将结果存储在多项式 `p2` 中。
 * 分别对每一个模数的多项式调用iNTTAndEqual()进行INTT。
 * @param p1 输入的多项式，类型为 `Poly*`，其系数将被进行 INTT 操作。
 * @param p2 输出的多项式，类型为 `Poly*`，存储 INTT 结果。
 */
void Ring::InvNTT(Poly *p1, Poly *p2)
{
    Copy(p1, p2);
#pragma omp parallel for
    for (size_t x = 0; x < Modulus.size(); ++x) {
        iINTTAndEqual(p2->Coeffs[x], x);
    }
}
/*
 * @brief 对多项式选择层数进行快速逆数论变换（INTT）。
 *
 * 该函数实现了对多项式 `p1` 的逆数论变换（INTT），并将结果存储在多项式 `p2` 中。
 * 分别对l个模数的多项式调用iINTTAndEqual()进行INTT。
 *
 * @param level 进行运算的最高层级(从0开始计数)
 * @param p1 输入的多项式，类型为 `Poly*`，其系数将被进行 INTT 操作。
 * @param p2 输出的多项式，类型为 `Poly*`，存储 INTT 结果。
 */
void Ring::InvNTTLvl(uint64_t level, Poly *p1, Poly *p2)
{
#pragma omp parallel for
    for (size_t x = 0; x < level + 1; x++) {
        iINTTAndEqual(p1->Coeffs[x], x);
    }
    Copy(p1, p2);
}
/*
 * @brief 对多项式进行数论变换（NTT）。
 *
 * 该函数实现了对多项式的数论变换（NTT）操作。
 * 该实现使用了 Shoup 模乘优化，以提高计算效率。
 * @param a 输入输出的多项式系数，类型为 `std::vector<uint64_t>`，在函数结束时将包含经过 NTT 变换后的结果。
 * @param index 用于选择对应模数和根的索引，指定了当前变换所使用的模数和根。
 */
void Ring::iNTTAndEqual(vector<uint64_t> &a, long index)
{
    long t = N;
    long logt1 = logN;
    uint64_t q = Modulus[index];
    int gap = N / 8;
    for (long m = 1; m < gap; m <<= 1) {
        t >>= 1;
        for (long i = 0; i < m; i++) {
            long j1 = i << logt1;
            long j2 = j1 + t;
            uint64_t W = qRootPows[index][m + i];
            uint64_t wShoup = qRootPowsShoup[index][m + i];
            uint64x2_t q2 = vdupq_n_u64(q);
            for (long j = j1; j < j2; j += 4) {
                __uint128_t vMul[4];
                uint64_t hi[4];
                uint64_t V[4];
                for (int k = 0; k < 4; k++) {
                    vMul[k] = static_cast<__uint128_t>(a[j + t + k]) * wShoup;
                    hi[k] = vMul[k] >> 64;
                    V[k] = a[j + t + k] * W - hi[k] * q;
                    if (V[k] >= q) {
                        V[k] -= q;
                    }
                }
                uint64x2_t vVec1 = vcombine_u64(vdup_n_u64(V[0]), vdup_n_u64(V[1]));
                uint64x2_t vVec2 = vcombine_u64(vdup_n_u64(V[2]), vdup_n_u64(V[3]));
                for (int k = 0; k < 2; k++) {
                    uint64x2_t ajtVec = vld1q_u64(&a[j + t + 2 * k]);
                    uint64x2_t ajVec = vld1q_u64(&a[j + 2 * k]);
                    uint64x2_t ajPlusQMinusV = vsubq_u64(vaddq_u64(ajVec, q2), k == 0 ? vVec1 : vVec2);
                    uint64x2_t lessMask = vcltq_u64(ajVec, k == 0 ? vVec1 : vVec2);
                    uint64x2_t ajtNew = vbslq_u64(lessMask, ajPlusQMinusV, vsubq_u64(ajVec, k == 0 ? vVec1 : vVec2));
                    vst1q_u64(&a[j + t + 2 * k], ajtNew);

                    ajVec = vaddq_u64(ajVec, k == 0 ? vVec1 : vVec2);
                    uint64x2_t gtMask = vcgeq_u64(ajVec, q2);
                    ajVec = vbslq_u64(gtMask, vsubq_u64(ajVec, q2), ajVec);
                    vst1q_u64(&a[j + 2 * k], ajVec);
                }
            }
        }
        logt1 -= 1;
    }

    for (long m = gap; m < N; m <<= 1) {
        t >>= 1;
        for (long i = 0; i < m; i++) {
            long j1 = i << logt1;
            long j2 = j1 + t;
            uint64_t W = qRootPows[index][m + i];
            uint64_t wShoup = qRootPowsShoup[index][m + i];
            for (long j = j1; j < j2; j++) {
                __uint128_t v_w_mul = static_cast<__uint128_t>(a[j + t]) * wShoup;
                uint64_t hi = v_w_mul >> 64;
                uint64_t V = a[j + t] * W - hi * q;
                if (V >= q) {
                    V -= q;
                }
                a[j + t] = a[j] < V ? a[j] + q - V : a[j] - V;
                a[j] += V;
                if (a[j] >= q) {
                    a[j] -= q;
                }
            }
        }
        logt1 -= 1;
    }
}

/*
 * @brief 对多项式进行逆数论变换（INTT）。
 *
 * 该函数实现了对多项式的逆数论变换（INTT）操作。
 * 该实现使用了 Shoup 模乘优化，以提高计算效率。
 * @param a 输入输出的多项式系数，类型为 `std::vector<uint64_t>`，在函数结束时将包含经过 INTT 变换后的结果。
 * @param index 用于选择对应模数和根的索引，指定了当前变换所使用的模数和根。
 */
void Ring::iINTTAndEqual(vector<uint64_t> &a, long index)
{
    uint64_t q = Modulus[index];
    uint64_t qd = qdVec[index];
    long t = 1;
    for (long m = N >> 1; m >= 1; m >>= 1) {
        long j1 = 0;
        for (long i = 0; i < m; i++) {
            long j2 = j1 + t - 1;
            uint64_t W = qRootPowsInv[index][m + i];
            uint64_t wShoup = qRootPowsInvShoup[index][m + i];

            for (long j = j1; j <= j2; j++) {
                uint64_t T = a[j] + qd;
                T -= a[j + t];
                a[j] += a[j + t];
                if (a[j] >= qd)
                    a[j] -= qd;
                // ------------shoup---------
                __uint128_t shoupMul = static_cast<__uint128_t>(T) * wShoup;
                uint64_t hi = shoupMul >> 64;
                uint64_t V = T * W - hi * q;
                if (V >= q)
                    V -= q;
                a[j + t] = V;
                // ------------shoup---------
            }
            j1 += (t << 1);
        }
        t <<= 1;
    }
    uint64_t NScale = nInvModq[index];
    uint64_t nScaleShoup = nInvModqShoup[index];

    for (long i = 0; i < N; i++) {
        uint64_t Ti = (a[i] < q) ? a[i] : a[i] - q;
        // ------------shoup---------
        __uint128_t shoupMul = static_cast<__uint128_t>(Ti) * nScaleShoup;
        uint64_t hi = shoupMul >> 64;
        uint64_t V = Ti * NScale - hi * q;
        if (V >= q)
            V -= q;
        a[i] = V;
        // ------------shoup---------
    }
}

/*
 * @brief 设置环的基本参数，包括度数、模数等
 *
 * 该函数初始化环的基本参数，包括：
 * - 验证并设置环的度数(必须是大于等于16的2的幂)
 * - 验证并设置模数向量
 * - 计算每个模数对应的掩码值
 * - 计算所有模数的乘积
 * - 计算Barrett归约参数
 *
 * @param nParam 环的度数，必须是大于等于16的2的幂
 * @param modulusParam 模数向量，包含所有互不相同的模数
 */
void Ring::SetParameters(uint64_t nParam, const std::vector<uint64_t> &modulusParam)
{
    if (nParam < 16 || ((nParam & (nParam - 1)) != 0 && nParam != 0)) {
        throw std::invalid_argument("invalid ring degree (must be a power of 2 >= 16)");
    }
    if (modulusParam.empty()) {
        throw std::invalid_argument("invalid modulus (must be a non-empty vector<uint64_t>)");
    }
    if (!AllDistinct(modulusParam)) {
        throw std::invalid_argument("invalid modulus (moduli are not distinct)");
    }
    allowsNTT = false;
    N = nParam;
    NthRoot = 2 * N;
    Modulus = modulusParam;
    Mask.resize(Modulus.size());
    for (size_t i = 0; i < Modulus.size(); ++i) {
        uint64_t qi = Modulus[i];
        uint64_t bit_length = 0;
        uint64_t temp_qi = qi;
        while (temp_qi > 0) {
            bit_length++;
            temp_qi >>= 1;
        }
        Mask[i] = (1ULL << bit_length) - 1;
    }
    modulusBigint = new mpz_class(1);
    for (const auto &qi : Modulus) {
        *modulusBigint *= NewUint(qi);
    }
    bredParams.resize(Modulus.size(), std::vector<uint64_t>());
    for (size_t i = 0; i < Modulus.size(); ++i) {
        uint64_t qi = Modulus[i];
        bredParams[i] = BRedParams(qi);
    }
}

Poly *Ring::NewPoly() const
{
    Poly *p = new Poly();
    p->Coeffs.resize(Modulus.size(), std::vector<uint64_t>(N, 0));
    return p;
}

Poly *Ring::NewPoly(int nbModulus)
{
    Poly *p = new Poly();
    p->Coeffs.resize(nbModulus, std::vector<uint64_t>(N, 0));
    return p;
}
/*
 * @brief 对多项式进行置换操作
 *
 * 该函数实现了基于生成元的多项式系数置换,用于同态加密中的 Galois 自同构运算。
 * 对于输入多项式的每个系数，根据生成元计算新的位置，并进行适当的符号调整。
 *
 * @param polIn 输入多项式指针
 * @param gen 用于置换的生成元
 * @param polOut 输出多项式指针，存储置换后的结果
 *
 * @details 具体实现步骤:
 * 1. 计算掩码 mask = N-1 用于索引范围控制
 * 2. 对每个系数位置 i:
 * - 计算原始新索引 indexRaw = i * gen
 * - 使用掩码获取实际索引 index = indexRaw & mask
 * - 根据 indexRaw 的高位确定符号调整
 * 3. 对每个模数:
 * - 根据符号位选择是保持原值还是取模减去原值
 */
void Ring::Permute(const Poly *polIn, uint64_t gen, Poly *polOut)
{
    uint64_t mask, index, indexRaw, logMask, tmp;

    mask = uint64_t(N - 1);

    logMask = uint64_t(BitsLen64(mask));

    for (uint64_t i = 0; i < N; i++) {
        indexRaw = i * gen;

        index = indexRaw & mask;

        tmp = (indexRaw >> logMask) & 1;

        for (uint64_t j = 0; j < Modulus.size(); j++) {
            uint64_t qi = Modulus[j];
            polOut->Coeffs[j][index] = polIn->Coeffs[j][i] * (tmp ^ 1) | (qi - polIn->Coeffs[j][i]) * tmp;
        }
    }
}
/*
 * @brief 复制一个多项式对象到另一个多项式对象
 *
 * @param[in] p0 源多项式指针
 * @param[out] p1 目标多项式指针
 *
 * @details 该函数将源多项式 p0 的所有系数复制到目标多项式 p1 中。在复制之前会进行一系列的有效性检查：
 * - 检查源和目标指针是否为空
 * - 检查源和目标是否为同一个对象
 * - 检查源和目标的系数集合数量是否相同
 * - 检查每个系数集合的大小是否符合环的度数要求*
 */
void Ring::Copy(const Poly *p0, Poly *p1)
{
    if (!p0 || !p1) {
        throw std::invalid_argument("p0 and p1 must not be null.");
    }
    if (p0 == p1) {
        return; // No action needed if both pointers are the same
    }
    if (p0->Coeffs.size() != p1->Coeffs.size()) {
        throw std::invalid_argument("p0 and p1 must have the same number of coefficient sets.");
    }
    for (size_t i = 0; i < Modulus.size(); ++i) {
        if (p0->Coeffs[i].size() != N || p1->Coeffs[i].size() < N) {
            throw std::invalid_argument("Coefficient sizes do not match the ring degree.");
        }
        for (uint64_t j = 0; j < N; ++j) {
            p1->Coeffs[i][j] = p0->Coeffs[i][j];
        }
    }
}
/*
 * @brief 对两个多项式进行模加运算
 *
 * @details 将两个多项式 p1 和 p2 按照系数逐项相加,结果存储在 p3 中。
 * 加法运算在所有可用的模数层级上进行,层级数由三个多项式的最小层级决定。
 *
 * @param p1 第一个输入多项式指针
 * @param p2 第二个输入多项式指针
 * @param p3 存储结果的多项式指针
 */
void Ring::Add(Poly *p1, Poly *p2, Poly *p3)
{
    AddLvl(MinLevelTernary(p1, p2, p3), p1, p2, p3);
}
/*
 * @brief 在指定层级对两个多项式进行模加运算
 *
 * @details 在给定的层级范围内,将两个多项式 p1 和 p2 按照系数逐项相加,结果存储在 p3 中。
 *
 * @param level 进行运算的最高层级(从0开始计数)
 * @param p1 第一个输入多项式指针
 * @param p2 第二个输入多项式指针
 * @param p3 存储结果的多项式指针
 */
void Ring::AddLvl(int level, Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for
    for (int i = 0; i < level + 1; i++) {
        for (int j = 0; j < N; j++) {
            uint64_t qi = Modulus[i];
            p3->Coeffs[i][j] = CRed(p1->Coeffs[i][j] + p2->Coeffs[i][j], qi);
        }
    }
}
/*
 * @brief 执行两个多项式的无模加法运算
 *
 * 该函数对两个多项式执行系数级别的加法运算，但不进行模约化。
 * 会自动选择输入多项式中最小的层级进行运算。
 *
 * @param p1 输入多项式指针
 * @param p2 输入多项式指针
 * @param p3 输出多项式指针，用于存储结果
 */
void Ring::AddNoMod(Poly *p1, Poly *p2, Poly *p3)
{
    AddNoModLvl(MinLevelTernary(p1, p2, p3), p1, p2, p3);
}
/*
 * @brief 在指定层级执行两个多项式的无模加法运算
 *
 * 该函数在指定层级对两个多项式执行系数级别的加法运算，但不进行模约化。
 * 对每个系数位置，直接将两个输入多项式的对应系数相加。
 *
 * @param level 指定要进行运算的层级
 * @param p1 输入多项式指针
 * @param p2 输入多项式指针
 * @param p3 输出多项式指针，用于存储结果
 */
void Ring::AddNoModLvl(int level, Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for collapse(2)
    for (int i = 0; i < level + 1; i++) {
        for (uint64_t j = 0; j < N; j++) {
            p3->Coeffs[i][j] = p1->Coeffs[i][j] + p2->Coeffs[i][j];
        }
    }
}
/*
 * @brief 对两个多项式进行模减运算
 *
 * @details 将两个多项式 p1 和 p2 按照系数逐项相减,结果存储在 p3 中。
 * 减法运算在所有可用的模数层级上进行,层级数由三个多项式的最小层级决定。
 *
 * @param p1 第一个输入多项式指针
 * @param p2 第二个输入多项式指针
 * @param p3 存储结果的多项式指针
 */
void Ring::Sub(Poly *p1, Poly *p2, Poly *p3)
{
    SubLvl(MinLevelTernary(p1, p2, p3), p1, p2, p3);
}
/*
 * @brief 在指定层级对两个多项式进行模减运算
 *
 * @details 在给定的层级范围内,将两个多项式 p1 和 p2 按照系数逐项相减,结果存储在 p3 中。
 *
 * @param level 进行运算的最高层级(从0开始计数)
 * @param p1 第一个输入多项式指针
 * @param p2 第二个输入多项式指针
 * @param p3 存储结果的多项式指针
 */
void Ring::SubLvl(int level, Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for collapse(2)
    for (int i = 0; i < level + 1; i++) {
        for (uint64_t j = 0; j < N; j++) {
            p3->Coeffs[i][j] = CRed((p1->Coeffs[i][j] + Modulus[i]) - p2->Coeffs[i][j], Modulus[i]);
        }
    }
}
/*
 * @brief 执行两个多项式的无模减法运算
 *
 * 该函数对两个多项式执行系数级别的减法运算，但不进行模约化。
 * 会自动选择输入多项式中最小的层级进行运算。
 *
 * @param p1 输入多项式指针
 * @param p2 输入多项式指针
 * @param p3 输出多项式指针，用于存储结果
 */
void Ring::SubNoMod(Poly *p1, Poly *p2, Poly *p3)
{
    SubNoModLvl(MinLevelTernary(p1, p2, p3), p1, p2, p3);
}
/*
 * @brief 在指定层级执行两个多项式的无模减法运算
 *
 * 该函数在指定层级对两个多项式执行系数级别的减法运算，但不进行模约化。
 * 对每个系数位置，直接将两个输入多项式的对应系数相减。
 *
 * @param level 指定要进行运算的层级
 * @param p1 输入多项式指针
 * @param p2 输入多项式指针
 * @param p3 输出多项式指针，用于存储结果
 */
void Ring::SubNoModLvl(int level, Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for collapse(2)
    for (int i = 0; i < level + 1; i++) {
        for (uint64_t j = 0; j < N; j++) {
            p3->Coeffs[i][j] = p1->Coeffs[i][j] + Modulus[i] - p2->Coeffs[i][j];
        }
    }
}
/*
 * @brief 计算多项式的负值。
 *
 * 该函数通过调用 `NegLvl` 函数，计算多项式 `p1` 的负值，并将结果存储在多项式 `p2` 中
 *
 * @param p1 输入的多项式，类型为 `Poly*`，包含需要计算负值的系数。
 * @param p2 输出的多项式，类型为 `Poly*`，存储计算得到的负值。
 */
void Ring::Neg(Poly *p1, Poly *p2)
{
    NegLvl(MinLevelBinary(p1, p2), p1, p2);
}
/*
 * @brief 计算多项式在指定层次上的负值。
 *
 * 该函数计算多项式 `p1` 在指定层次 `level` 上的负值，并将结果存储在多项式 `p2` 中。
 * 对于每个层次 `i`，负值通过 `Modulus[i] - p1->Coeffs[i][j]` 计算，保证了结果在模数范围内。
 *
 * @param level 需要计算负值的层次，类型为 `int`，确定了负值计算的层数。
 * @param p1 输入的多项式，类型为 `Poly*`，包含需要计算负值的系数。
 * @param p2 输出的多项式，类型为 `Poly*`，存储计算得到的负值。
 */
void Ring::NegLvl(int level, Poly *p1, Poly *p2)
{
#pragma omp parallel for collapse(2)
    for (int i = 0; i < level + 1; i++) {
        for (uint64_t j = 0; j < N; j++) {
            p2->Coeffs[i][j] = Modulus[i] - p1->Coeffs[i][j];
        }
    }
}
/*
 * @brief 对多项式的系数执行模约简操作。
 *
 * 该函数对输入多项式 `p1` 的系数执行模约简操作，并将结果存储到 `p2` 中。
 * 它通过调用 `ReduceLvl` 函数对最小层的系数进行处理。模约简使用了 Barrett 减法以确保结果在模数范围内。
 *
 * @param p1 输入的多项式，类型为 `Poly*`，其系数将进行模约简。
 * @param p2 输出的多项式，类型为 `Poly*`，存储约简后的系数。
 */
void Ring::Reduce(Poly *p1, Poly *p2)
{
    ReduceLvl(MinLevelBinary(p1, p2), p1, p2);
}
/*
 * @brief 在指定层次上对多项式系数进行模约简。
 *
 * 该函数对输入多项式 `p1` 的每一层系数执行 Barrett 减法，并将约简后的结果存储到 `p2` 中。
 *
 * @param level 约简操作的层级，指定应处理多少层系数。
 * @param p1 输入的多项式，类型为 `Poly*`，其系数将进行模约简。
 * @param p2 输出的多项式，类型为 `Poly*`，存储约简后的系数。
 *
 * @see BRedAdd
 */
void Ring::ReduceLvl(uint64_t level, Poly *p1, Poly *p2)
{
#pragma omp parallel for collapse(2)
    for (int i = 0; i < level + 1; i++) {
        for (uint64_t j = 0; j < N; j++) {
            p2->Coeffs[i][j] = BRedAdd(p1->Coeffs[i][j], Modulus[i], bredParams[i]);
        }
    }
}
/*
 * @brief 计算两个多项式系数的 Barrett 乘法。
 *
 * 该函数计算两个多项式 `p1` 和 `p2` 的系数逐项相乘并存储在多项式 `p3` 中。
 * 计算过程中使用 Barrett 乘法来执行模运算，从而确保结果在相应模数下的正确性。
 * 计算是通过调用 `MulCoeffsBarrettLvl` 函数来完成的。
 *
 * @param p1 输入的第一个多项式，类型为 `Poly*`，其系数与 `p2` 的系数逐项相乘。
 * @param p2 输入的第二个多项式，类型为 `Poly*`，其系数与 `p1` 的系数逐项相乘。
 * @param p3 输出的多项式，类型为 `Poly*`，存储计算得到的乘积结果。
 */
void Ring::MulCoeffsBarrett(Poly *p1, Poly *p2, Poly *p3)
{
    MulCoeffsBarrettLvl(MinLevelTernary(p1, p2, p3), p1, p2, p3);
}
/*
 * @brief 计算两个多项式在指定层次上的 Barrett 乘法。
 *
 * 该函数计算两个多项式 `p1` 和 `p2` 在指定层次 `level` 上的系数逐项相乘并存储在多项式 `p3` 中。
 * 乘法过程中使用 Barrett 乘法来执行模运算，确保结果在相应模数下的正确性。
 *
 * @param level 需要计算 Barrett 乘法的层次，类型为 `uint64_t`，指定了操作的层数。
 * @param p1 输入的第一个多项式，类型为 `Poly*`，其系数与 `p2` 的系数逐项相乘。
 * @param p2 输入的第二个多项式，类型为 `Poly*`，其系数与 `p1` 的系数逐项相乘。
 * @param p3 输出的多项式，类型为 `Poly*`，存储计算得到的乘积结果。
 */

void Ring::MulCoeffsBarrettLvl(uint64_t level, Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for
    for (uint64_t i = 0; i < level + 1; ++i) {
        uint64_t qi = Modulus[i];
        vector<uint64_t> mBredParams = bredParams[i];
        auto &p1tmp = p1->Coeffs[i];
        auto &p2tmp = p2->Coeffs[i];
        auto &p3tmp = p3->Coeffs[i];
        for (uint64_t j = 0; j < N; ++j) {
            MulModBarrett(p3tmp[j], p1tmp[j], p2tmp[j], qi, mBredParams[2], mBredParams[0], mBredParams[1]);
        }
    }
}

// void Ring::MulCoeffsBarrettLvl(uint64_t level, Poly *p1, Poly *p2, Poly *p3)
// {
// #pragma omp parallel for collapse(2)
//     for (uint64_t i = 0; i < level + 1; ++i) {

//         for (uint64_t j = 0; j < N; ++j) {
//             uint64_t qi = Modulus[i];
//             vector<uint64_t> mBredParams = bredParams[i];
//             auto &p1tmp = p1->Coeffs[i];
//             auto &p2tmp = p2->Coeffs[i];
//             auto &p3tmp = p3->Coeffs[i];
//             MulModBarrett(p3tmp[j], p1tmp[j], p2tmp[j], qi, mBredParams[2], mBredParams[0], mBredParams[1]);
//         }
//     }
// }
/*
 * @brief 计算两个多项式系数的 Barrett 乘法并加到第三个多项式上。
 *
 * 该函数计算两个多项式 `p1` 和 `p2` 的系数逐项相乘，并将结果加到多项式 `p3` 的对应系数上。
 * 乘法过程中使用 Barrett 乘法来执行模运算，从而确保结果在相应模数下的正确性。
 *
 * @param p1 输入的第一个多项式，类型为 `Poly*`，其系数将与 `p2` 的系数逐项相乘。
 * @param p2 输入的第二个多项式，类型为 `Poly*`，其系数将与 `p1` 的系数逐项相乘。
 * @param p3 输出的多项式，类型为 `Poly*`，在计算中存储加法结果。每个计算结果是 `p1[i] * p2[i] + p3[i]`，并应用模运算。
 */
void Ring::MulCoeffsBarrettAndAdd(Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for
    for (size_t i = 0; i < Modulus.size(); ++i) {
        uint64_t qi = Modulus[i];
        vector<uint64_t> mBredParams = bredParams[i];
        auto &p1tmp = p1->Coeffs[i];
        auto &p2tmp = p2->Coeffs[i];
        auto &p3tmp = p3->Coeffs[i];
        uint64_t tmp;
        for (uint64_t j = 0; j < N; ++j) {
            MulModBarrett(tmp, p1tmp[j], p2tmp[j], qi, mBredParams[2], mBredParams[0], mBredParams[1]);
            AddMod(p3tmp[j], p3tmp[j], tmp, qi);
        }
    }
}
/*
 * @brief 对多项式系数执行 Barrett 乘法并加法，不进行模操作。
 *
 * 该函数对输入的多项式 `p1` 和 `p2` 的每一层系数执行 Barrett 乘法，并将结果加到多项式 `p3` 对应的系数上。
 * 与标准的 Barrett 乘法不同，该函数不执行最终的模约简操作。它主要用于计算乘积并累加结果，而不进行模约简。
 *
 * @param p1 输入的多项式，类型为 `Poly*`，与 `p2` 中的多项式相乘。
 * @param p2 输入的多项式，类型为 `Poly*`，与 `p1` 中的多项式相乘。
 * @param p3 输出的多项式，类型为 `Poly*`，存储累加后的结果。
 */
void Ring::MulCoeffsBarrettAndAddNoMod(Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for
    for (size_t i = 0; i < Modulus.size(); ++i) {
        uint64_t qi = Modulus[i];
        vector<uint64_t> mBredParams = bredParams[i];
        uint64_t tmp;
        auto &p1tmp = p1->Coeffs[i];
        auto &p2tmp = p2->Coeffs[i];
        auto &p3tmp = p3->Coeffs[i];
        for (uint64_t j = 0; j < N; ++j) {
            MulModBarrett(tmp, p1tmp[j], p2tmp[j], qi, mBredParams[2], mBredParams[0], mBredParams[1]);
            p3tmp[j] += tmp;
        }
    }
}
/*
 * @brief 对多项式系数执行 Barrett 乘法并减法。
 *
 * 该函数对输入的多项式 `p1` 和 `p2` 执行 Barrett 乘法，并将结果从 `p3` 对应的系数中减去。
 * 该操作会在每一层对系数进行 Barrett 乘法，然后进行减法操作。与标准的 Barrett 乘法不同，此函数通过减法将乘积结果从 `p3`
 * 中去除。
 *
 * @param level 要处理的多项式级别。
 * @param p1 输入的多项式，类型为 `Poly*`，与 `p2` 中的多项式相乘。
 * @param p2 输入的多项式，类型为 `Poly*`，与 `p1` 中的多项式相乘。
 * @param p3 输出的多项式，类型为 `Poly*`，存储减去乘积结果的最终值。
 */
void Ring::MulCoeffsBarrettAndSubLvl(int level, Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for
    for (int i = 0; i < level + 1; i++) {
        uint64_t qi = Modulus[i];
        vector<uint64_t> mBredParams = bredParams[i];
        uint64_t tmp;
        for (int j = 0; j < N; j++) {
            MulModBarrett(tmp, p1->Coeffs[i][j], p2->Coeffs[i][j], qi, mBredParams[2], mBredParams[0], mBredParams[1]);
            SubMod(p3->Coeffs[i][j], p3->Coeffs[i][j], tmp, qi);
        }
    }
}
/*
 * @brief 对多项式系数执行 Barrett 乘法并将结果存储到 `p3` 中。
 *
 * 该函数对输入的多项式 `p1` 和 `p2` 执行 Barrett 乘法，将结果存储到输出的多项式 `p3` 中。
 * 与常规 Barrett 乘法不同，这个版本的函数使用常量参数来进行乘法计算。该操作对每一层的系数进行 Barrett
 * 乘法，输出结果存储到 `p3` 中的相应位置。
 *
 * @param p1 输入的多项式，类型为 `Poly*`，包含待乘的系数。
 * @param p2 输入的多项式，类型为 `Poly*`，包含待乘的系数。
 * @param p3 输出的多项式，类型为 `Poly*`，存储乘法结果。
 */
void Ring::MulCoeffsBarrettConstant(Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for
    for (size_t i = 0; i < Modulus.size(); ++i) {
        uint64_t qi = Modulus[i];
        auto &p1tmp = p1->Coeffs[i];
        auto &p2tmp = p2->Coeffs[i];
        auto &p3tmp = p3->Coeffs[i];
        for (uint64_t j = 0; j < N; ++j) {
            MulModBarrett(p3tmp[j], p1tmp[j], p2tmp[j], qi, bredParams[i][2], bredParams[i][0], bredParams[i][1]);
        }
    }
}
/*
 * @brief 计算两个多项式系数的乘积并将结果存储到第三个多项式中。
 *
 * 此函数会调用 `MulCoeffsLvl`，基于传入的最小级别值来执行乘法运算。
 *
 * @param p1 第一个多项式，存储待相乘的系数。
 * @param p2 第二个多项式，存储待相乘的系数。
 * @param p3 结果多项式，存储乘积结果的系数。
 */
void Ring::MulCoeffs(Poly *p1, Poly *p2, Poly *p3)
{
    MulCoeffsLvl(MinLevelTernary(p1, p2, p3), p1, p2, p3);
}
/*
 * @brief 计算两个多项式系数的乘积，并根据给定的级别进行处理。
 *
 * 对于给定的级别 `level`，此函数逐层计算两个多项式的系数乘积，并存储到第三个多项式中。
 *
 * @param level 多项式乘法的级别，表示需要处理的层数。
 * @param p1 第一个多项式，存储待相乘的系数。
 * @param p2 第二个多项式，存储待相乘的系数。
 * @param p3 结果多项式，存储乘积结果的系数。
 */
void Ring::MulCoeffsLvl(int level, Poly *p1, Poly *p2, Poly *p3)
{
    for (int i = 0; i < level + 1; i++) {
        uint64_t qi = Modulus[i];
        for (uint64_t j = 0; j < N; j++) {
            MulModBarrett(p3->Coeffs[i][j], p1->Coeffs[i][j], p2->Coeffs[i][j], qi, bredParams[i][2], bredParams[i][0],
                bredParams[i][1]);
        }
    }
}
/*
 * @brief 将大整数标量加到多项式的每个系数上。
 *
 * 本函数将一个大整数标量加到多项式 `p1` 的每个系数上，并将结果存储在多项式 `p2` 中。
 * 每个系数的加法运算会在相应的模数下进行，确保结果符合该模数。
 *
 * @param p1 输入的多项式，存储加法操作中的原始系数。
 * @param scalar 要加到多项式系数上的大整数标量。
 * @param p2 输出的多项式，存储加法操作后的结果。
 */
void Ring::AddScalarBigint(Poly *p1, mpz_class scalar, Poly *p2)
{
    mpz_class tmp;
    for (size_t i = 0; i < Modulus.size(); ++i) {
        uint64_t Qi = Modulus[i];
        tmp = scalar % Qi;
        uint64_t scalarQi = tmp.get_ui();
        auto &p1tmp = p1->Coeffs[i];
        auto &p2tmp = p2->Coeffs[i];
    #pragma omp parallel for
        for (uint64_t j = 0; j < N; ++j) {
            p2tmp[j] = CRed(p1tmp[j] + scalarQi, Qi);
        }
    }
}
/*
 * @brief 从多项式的每个系数中减去一个大整数标量。
 *
 * 本函数将一个大整数标量从多项式 `p1` 的每个系数中减去，并将结果存储在多项式 `p2` 中。
 * 每个系数的减法运算会在相应的模数下进行，确保结果符合该模数。
 *
 * @param p1 输入的多项式，存储减法操作中的原始系数。
 * @param scalar 要从多项式系数中减去的大整数标量。
 * @param p2 输出的多项式，存储减法操作后的结果。
 */
void Ring::SubScalarBigint(Poly *p1, mpz_class scalar, Poly *p2)
{
    mpz_class tmp;
    for (size_t i = 0; i < Modulus.size(); ++i) {
        uint64_t Qi = Modulus[i];
        tmp = scalar % NewUint(Qi);
        uint64_t scalarQi = tmp.get_ui();
        auto &p1tmp = p1->Coeffs[i];
        auto &p2tmp = p2->Coeffs[i];
        for (uint64_t j = 0; j < N; ++j) {
            p2tmp[j] = CRed(p1tmp[j] + Qi - scalarQi, Qi);
        }
    }
}
/*
 * @brief 将标量乘到多项式的每个系数上。
 *
 * 本函数将一个标量乘到多项式 `p1` 的每个系数上，并将结果存储在多项式 `p2` 中。
 * 该操作会根据 `MinLevelBinary` 来决定处理的层级。
 *
 * @param p1 输入的多项式，存储需要乘标量的系数。
 * @param scalar 要乘的标量。
 * @param p2 输出的多项式，存储乘法操作后的结果。
 */
void Ring::MulScalar(Poly *p1, uint64_t scalar, Poly *p2)
{
    MulScalarLvl(MinLevelBinary(p1, p2), p1, scalar, p2);
}

/*
 * @brief 计算多项式的每个系数与标量的乘积，并根据给定的级别进行处理。
 *
 * 对于给定的级别 `level`，此函数逐层计算多项式的每个系数与标量的乘积，并将结果存储到第三个多项式中。
 *
 * @param level 多项式乘法的级别，表示需要处理的层数。
 * @param p1 输入的多项式，存储待相乘的系数。
 * @param scalar 要乘的标量。
 * @param p2 输出的多项式，存储乘法操作后的结果。
 */
void Ring::MulScalarLvl(int level, Poly *p1, uint64_t scalar, Poly *p2)
{
    #pragma omp parallel for
    for (int i = 0; i < level + 1; i++) {
        for (uint64_t j = 0; j < N; j++) {
            MulModBarrett(p2->Coeffs[i][j], p1->Coeffs[i][j], scalar + Modulus[i], Modulus[i], bredParams[i][2],
                bredParams[i][0], bredParams[i][1]);
        }
    }
}

/*
 * @brief 将标量与多项式的每个系数进行 Barrett 模乘法运算。
 *
 * 本函数首先调用 `MulScalarLvlBarrett` 对多项式 `p1` 的每个系数与给定标量进行 Barrett 模乘法运算，结果存储在多项式 `p2`
 * 中。
 *
 * @param p1 输入的多项式，存储需要进行乘法的系数。
 * @param scalar 要乘的标量。
 * @param p2 输出的多项式，存储乘法操作后的结果。
 */
void Ring::MulScalarBarrett(Poly *p1, uint64_t scalar, Poly *p2)
{
    MulScalarLvlBarrett(MinLevelBinary(p1, p2), p1, scalar, p2);
}
/*
 * @brief 将标量与多项式的每个系数进行 Barrett 模乘法运算，支持多层级别。
 *
 * 本函数在给定的 `level` 层级上对多项式 `p1` 的每个系数与标量进行 Barrett 模乘法运算，并将结果存储在多项式 `p2` 中。
 *
 * @param level 多项式乘法的层级，表示需要处理的层数。
 * @param p1 输入的多项式，存储待相乘的系数。
 * @param scalar 要乘的标量。
 * @param p2 输出的多项式，存储乘法操作后的结果。
 */
void Ring::MulScalarLvlBarrett(int level, Poly *p1, uint64_t scalar, Poly *p2)
{
#pragma omp parallel for
    for (int i = 0; i < level + 1; i++) {
        for (uint64_t j = 0; j < N; j++) {
            MulModBarrett(p2->Coeffs[i][j], p1->Coeffs[i][j], scalar + Modulus[i], Modulus[i], bredParams[i][2],
                bredParams[i][0], bredParams[i][1]);
        }
    }
}

RingQP::RingQP(Ring *ringQ, Ring *ringP) : ringQ(ringQ), ringP(ringP) {}

/* *
 * @brief 扩展基于小范数并进行中心化
 *
 * 该函数将输入多项式 `polyInQ` 扩展(复制)到指定的基 `polyOutQ`，并根据特定的规则更新 `polyOutP`。
 * - `coeff` 是输入多项式 `polyInQ` 中的系数。
 * - `Q` 和 `QHalf` 分别是环 `Q` 的模数及其一半。
 * - 如果输入系数 `coeff` 大于 `QHalf`，则调整其值为 `Q - coeff`，并标记符号 `sign` 为 0。
 * - 对于每个级别的模数 `pi`，根据 `coeff` 和符号 `sign` 计算 `polyOutP` 中的系数。
 * - 最终将调整后的系数存储到 `polyOutP` 中。
 *
 * @param polyInQ 输入多项式 `polyInQ`，包含要扩展和处理的系数。
 * @param levelP 模数的级别，控制输出多项式的处理精度。
 * @param polyOutQ 输出多项式 `polyOutQ`，存储扩展后的结果。
 * @param polyOutP 输出多项式 `polyOutP`，存储中心化后的结果。

 */
void RingQP::ExtendBasisSmallNormAndCenter(Poly *polyInQ, int levelP, Poly *polyOutQ, Poly *polyOutP)
{
    // uint64_t coeff, Q, QHalf, sign;
    uint64_t Q = ringQ->Modulus[0];
    uint64_t QHalf = Q >> 1;

    if (polyInQ != polyOutQ && polyOutQ != nullptr) {
        polyOutQ->Copy(polyInQ);
    }
#pragma omp parallel for
    for (int j = 0; j < ringQ->N; j++) {
        uint64_t coeff = polyInQ->Coeffs[0][j];
        uint64_t sign = 1;
        if (coeff > QHalf) {
            coeff = Q - coeff;
            sign = 0;
        }

        for (int i = 0; i < levelP + 1; i++) {
            uint64_t pi = ringP->Modulus[i];
            polyOutP->Coeffs[i][j] = (coeff * sign) | (pi - coeff) * (sign ^ 1);
        }
    }
}

int Ring::MinLevelBinary(Poly *p1, Poly *p2)
{
    return std::min(std::min(int(Modulus.size() - 1), p1->Level()), p2->Level());
}

int Ring::MinLevelTernary(Poly *p1, Poly *p2, Poly *p3)
{
    return std::min(std::min(int(Modulus.size() - 1), p1->Level()), std::min(p2->Level(), p3->Level()));
}

Poly *Ring::NewPolyLvl(int level)
{
    Poly *p = new Poly();
    p->Coeffs.resize(level + 1, std::vector<uint64_t>(N, 0));
    return p;
}
/*
 * @brief 计算NTT置换的索引映射
 *
 * 该函数计算用于NTT(数论变换)中的置换索引。它实现了基于Galois自同构的置换映射,
 * 具体步骤如下:
 * 1. 计算NthRoot的比特长度并创建掩码
 * 2. 对每个位置i:
 * - 计算位反转后的奇数位置
 * - 应用Galois元素进行变换
 * - 计算最终的置换位置
 *
 * @param galEl Galois元素,用于确定置换方式的参数
 * @return std::vector<uint64_t> 返回包含置换后索引的向量,大小为N
 */
std::vector<uint64_t> Ring::PermuteNTTIndex(uint64_t galEl)
{
    uint64_t mask, tmp1, tmp2, logNthRoot;
    logNthRoot = uint64_t(BitsLen64(NthRoot) - 2);
    mask = NthRoot - 1;
    std::vector<uint64_t> index(N);

    for (uint64_t i = 0; i < uint64_t(N); i++) {
        tmp1 = 2 * BitReverse64(i, logNthRoot) + 1;
        tmp2 = ((galEl * tmp1 & mask) - 1) >> 1;
        index[i] = BitReverse64(tmp2, logNthRoot);
    }
    return index;
}
/*
 * @brief 根据给定索引对多项式在NTT域中进行置换操作
 *
 * @param level 要处理的最高层级(从0开始计数)
 * @param polIn 输入多项式指针，包含待置换的系数
 * @param index 置换索引向量，存储了新旧位置的映射关系
 * @param polOut 输出多项式指针，用于存储置换后的结果
 */
void Ring::PermuteNTTWithIndexLvl(int level, Poly *polIn, std::vector<uint64_t> &index, Poly *polOut)
{
    for (uint64_t j = 0; j < N; j = j + 8) {
        for (auto i = 0; i < level + 1; i++) {
            polOut->Coeffs[i][j] = polIn->Coeffs[i][index[j]];
            polOut->Coeffs[i][j + 1] = polIn->Coeffs[i][index[j + 1]];
            polOut->Coeffs[i][j + 2] = polIn->Coeffs[i][index[j + 2]];
            polOut->Coeffs[i][j + 3] = polIn->Coeffs[i][index[j + 3]];
            polOut->Coeffs[i][j + 4] = polIn->Coeffs[i][index[j + 4]];
            polOut->Coeffs[i][j + 5] = polIn->Coeffs[i][index[j + 5]];
            polOut->Coeffs[i][j + 6] = polIn->Coeffs[i][index[j + 6]];
            polOut->Coeffs[i][j + 7] = polIn->Coeffs[i][index[j + 7]];
        }
    }
}
/*
 * @brief 对多项式的系数进行大整数标量乘法。
 *
 * @param level 操作的级别，决定了需要处理的多项式系数的数量。
 * @param p1 输入的多项式，其系数将与标量相乘。
 * @param scalar 输入的大整数标量。
 * @param p2 输出的多项式，保存乘法结果。
 */
void Ring::MulScalarBigintLvl(int level, Poly *p1, mpz_class *scalar, Poly *p2)
{
    mpz_class scalarQi;
    for (int i = 0; i < level + 1; i++) {
        scalarQi = *scalar % Modulus[i];
        MulScalarBarrettVec(p1->Coeffs[i], p2->Coeffs[i], BRedAdd(scalarQi.get_ui(), Modulus[i], bredParams[i]),
            Modulus[i], N);
    }
}
/*
 * @brief 执行带常数的 Barrett 乘法。
 *
 * @param level 操作的级别，决定了需要处理的多项式系数的数量。
 * @param p1 第一个输入多项式。
 * @param p2 第二个输入多项式。
 * @param p3 输出的多项式，保存乘法结果。
 */
void Ring::MulCoeffsBarrettConstantLvl(int level, Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for
    for (int i = 0; i < level + 1; i++) {
        MulCoeffsBarrettConstantVec(p1->Coeffs[i], p2->Coeffs[i], p3->Coeffs[i], Modulus[i], N);
    }
}
/*
 * @brief 执行带常数的 Barrett 乘法并加法，不进行模运算。
 *
 * @param level 操作的级别，决定了需要处理的多项式系数的数量。
 * @param p1 第一个输入多项式。
 * @param p2 第二个输入多项式。
 * @param p3 输出的多项式，保存乘法并加法的结果。
 */
void Ring::MulCoeffsBarrettConstantAndAddNoModLvl(int level, Poly *p1, Poly *p2, Poly *p3)
{
#pragma omp parallel for
    for (int i = 0; i < level + 1; i++) {
        MulCoeffsBarrettConstantAndAddNoModVec(p1->Coeffs[i], p2->Coeffs[i], p3->Coeffs[i], Modulus[i], N);
    }
}

PolyQP *RingQP::NewPoly()
{
    return new PolyQP(ringQ->NewPoly(), ringP->NewPoly());
}

PolyQP *RingQP::NewPolyLvl(int levelQ, int levelP)
{
    return new PolyQP(ringQ->NewPolyLvl(levelQ), ringP->NewPolyLvl(levelP));
}
/*
 * @brief 执行 NTT变换。
 *
 * 本函数依次对多项式 `p` 中的 Q 和 P 分量进行 NTT 变换，分别调用 `ringQ` 和 `ringP` 对应的 `NTTLvl` 函数。
 * 最终将结果存储在 `pOut` 中的 Q 和 P 分量中。
 *
 * @param levelQ Q 分量的 NTT 变换级别。
 * @param levelP P 分量的 NTT 变换级别。
 * @param p 输入的多项式，包含 Q 和 P 分量。
 * @param pOut 输出的多项式，存储变换后的 Q 和 P 分量。
 */
void RingQP::NTTLazyLvl(int levelQ, int levelP, PolyQP *p, PolyQP *pOut)
{
    ringQ->NTTLvl(levelQ, p->Q, pOut->Q);
    ringP->NTTLvl(levelP, p->P, pOut->P);
}
/*
 * @brief 对多项式 `p1` 和 `p2` 的系数执行 Barrett 乘法并减法操作，并将结果存储到多项式 `p3` 中。
 *
 * @param levelQ `Q` 部分操作的级别，决定了需要处理的多项式系数的数量。
 * @param levelP `P` 部分操作的级别，决定了需要处理的多项式系数的数量。
 * @param p1 输入的多项式，包含 `Q` 和 `P` 部分的系数。
 * @param p2 输入的多项式，包含 `Q` 和 `P` 部分的系数。
 * @param p3 输出的多项式，保存 Barrett 乘法并减法后的结果。
 */
void RingQP::MulCoeffsBarrettAndSubLvl(int levelQ, int levelP, PolyQP *p1, PolyQP *p2, PolyQP *p3)
{
    ringQ->MulCoeffsBarrettAndSubLvl(levelQ, p1->Q, p2->Q, p3->Q);
    ringP->MulCoeffsBarrettAndSubLvl(levelP, p1->P, p2->P, p3->P);
}
/*
 * @brief 对多项式 `p1` 和 `p2` 的系数执行 Barrett 乘法，并将结果存储到多项式 `p3` 中.
 *
 * @param levelQ `Q` 部分操作的级别，决定了需要处理的多项式系数的数量。
 * @param levelP `P` 部分操作的级别，决定了需要处理的多项式系数的数量。
 * @param p1 输入的多项式，包含 `Q` 和 `P` 部分的系数。
 * @param p2 输入的多项式，包含 `Q` 和 `P` 部分的系数。
 * @param p3 输出的多项式，保存 Barrett 乘法后的结果。
 */
void RingQP::MulCoeffsBarrettConstantLvl(int levelQ, int levelP, PolyQP *p1, PolyQP *p2, PolyQP *p3)
{
    ringQ->MulCoeffsBarrettConstantLvl(levelQ, p1->Q, p2->Q, p3->Q);
    ringP->MulCoeffsBarrettConstantLvl(levelP, p1->P, p2->P, p3->P);
}
/*
 * @brief 对多项式 `p1` 和 `p2` 的系数执行 Barrett 乘法并加法操作，不进行模运算，并将结果存储到多项式 `p3` 中。
 *
 * @param levelQ `Q` 部分操作的级别，决定了需要处理的多项式系数的数量。
 * @param levelP `P` 部分操作的级别，决定了需要处理的多项式系数的数量。
 * @param p1 输入的多项式，包含 `Q` 和 `P` 部分的系数。
 * @param p2 输入的多项式，包含 `Q` 和 `P` 部分的系数。
 * @param p3 输出的多项式，保存 Barrett 乘法并加法后的结果。
 */
void RingQP::MulCoeffsBarrettConstantAndAddNoModLvl(int levelQ, int levelP, PolyQP *p1, PolyQP *p2, PolyQP *p3)
{
    ringQ->MulCoeffsBarrettConstantAndAddNoModLvl(levelQ, p1->Q, p2->Q, p3->Q);
    ringP->MulCoeffsBarrettConstantAndAddNoModLvl(levelP, p1->P, p2->P, p3->P);
}